<?php
// +----------------------------------------------------------------------
// | Description: 资金操作类，该类比较特殊，可在服务层使用，必须开启事务 - Finance
// +----------------------------------------------------------------------
// | Author: liaoyizhi <liaoyizhi@gmail.com>
// +----------------------------------------------------------------------
// | Update: Markus <i@yoyoyo.me>
// +----------------------------------------------------------------------

namespace codelord\finance;

use codelord\finance\model\{
    Bill,
    BillSum,
    BillMath,
    CurrencyBalance
};

use think\{Db,Exception};

class Finance {

    /**
     * 对资金进行操作时，唯一的对外接口，一次调用仅针对一个用户操作
     * @param array $currency  各种货币消费组装数组，示例：
     *      $currency = [
     *          'uid' => 12,                                // 用户uid
     *          'number' => '2017031950484954',             // 订单中的订单号（无订单则需要生成新的）
     *          'bill_type_id' => 1,                        // 对应bill_type表中的id
     *          'currency' => [                             // 对应bill_math表中的顺序，传入正确的货币数组
     *              0 => [1, 100, 0, 'reason0'],            // 第一种货币，货币id是1，可用金额100，冻结金额0，备注为reason0（可用金额及冻结金额不要带符号，符号处理已经在bill_math表中规定）
     *              1 => [10000044, 1000, 0],               // 第二种货币，货币id是10000044，可用金额1000，冻结金额0（备注可为空或省略，将自动填充bill_math表中的备注）
     *              2 => [...]                              // 更多货币
     *          ],
     *          'op_person' => 12                           // 操作者uid（用户操作则为用户uid，管理员操作则为管理员uid）
     *      ];
     * @return void
     */
    public function bill(array $currency) : void
    {

//        if (!$pdo || !$pdo->inTransaction()) {
//            throw new Exception('敏感操作，请先开启事务', 10006);
//        }

        // 检测资金是否安全
        $this->checkSafety($currency['uid']);

        // 根据$bill_type_id获取此操作涉及到的算法
        $BillMath = new BillMath();

        $all_math = $BillMath->getMathByTypeId($currency['bill_type_id']);

        if (empty($all_math)) {
            throw new Exception("未找到相应算法类型{$currency['bill_type_id']}，操作失败", 20001);
        }

        // 检测余额是否充足
        $CurrencyBalance = new CurrencyBalance();

        $CurrencyBalance->checkCurrencyIsEnough($currency, $all_math);

        // 根据算法，处理货币符号
        $new_currency = $BillMath->getCurrencyMath($currency, $all_math);

        // 更新余额
        $CurrencyBalance->updateCurrencyBalance($new_currency);

        // 更新货币记录总和
        $BillSum = new BillSum();

        $new_currency['number'] = $currency['number'];
        $new_currency['bill_type_id'] = $currency['bill_type_id'];
        $new_currency['op_person'] = $currency['op_person'];

        $BillSum->updateBillSum($new_currency);

        // 获取用户现有货币信息
        $user_balance = $CurrencyBalance->getUserBalance($currency['uid']);

        // 插入消费记录
        $Bill = new Bill();

        $Bill->insertBill($new_currency, $user_balance);
    }

    /**
     * 检测用户的货币消费信息,根据公式加减后是否正确
     * @param int $uid  用户id的主键值
     * @return void
     */
    public function checkSafety(int $uid) : void
    {
        if ($uid < 0) {
            throw new Exception('参数丢失', 20002);
        }

        // 获取用户现有货币信息
        $cbLogic = new CurrencyBalance();

        $user_currency = $cbLogic->getUserBalance($uid);

        // 取得该用户的各类消费记录总和
        $bsLogic = new BillSum();

        foreach ($user_currency as $currency) {
            $currency_info = $bsLogic->getBillSum($uid, $currency['currency_id']);
            if (bccomp($currency_info['sum'], $currency['amount'], 2) != 0 || bccomp($currency_info['lock_sum'], $currency['amount_lock'], 2) != 0) {
                throw new Exception('您的资金存安全风险，请及时联系客服！', 20003);
            }
        }
    }
}
